package org.ytor.core.sqlflow.dsl.expr;

import lombok.extern.slf4j.Slf4j;
import org.ytor.core.sqlflow.dsl.expr.node.support.*;
import org.ytor.core.sqlflow.dsl.token.Token;
import org.ytor.core.sqlflow.dsl.token.TokenType;
import org.ytor.common.util.Numbers;
import org.ytor.core.sqlflow.dsl.expr.node.ExprNode;

import java.util.ArrayList;
import java.util.List;

/**
 * created by yangtong on 2025/8/6 19:53:04
 * <br/>
 * AST 解析器，递归下降
 */
@Slf4j
public class ExprAstParser {
    private final List<Token> tokens;
    private int pos = 0;
    private Boolean debugFlag;

    public ExprAstParser(List<Token> tokens) {
        this.tokens = tokens;
        this.debugFlag = false;
    }

    public ExprAstParser(List<Token> tokens, Boolean debugFlag) {
        this.tokens = tokens;
        this.debugFlag = debugFlag;
        if (debugFlag) {
            System.out.println("Tokens:");
            for (int i = 0; i < tokens.size(); i++) {
                System.out.printf("[%d] %s (%s)%n", i, tokens.get(i).type, tokens.get(i).literal);
            }
        }
    }


    public ExprNode parseExpression() {
        ExprNode expr = parseOrExpr();
        if (!isAtEnd() && peek().type != TokenType.RPAREN) {
            throw new RuntimeException("token解析失败，语法错误： " + peek() + ",at [" + pos + "]");
        }
        if (debugFlag) {
            System.out.println("Parse finished at pos = " + pos + ", token = " + peek());
        }
        return expr;
    }

    private ExprNode parseOrExpr() {
        log("parseOrExpr");
        ExprNode left = parseAndExpr();
        while (match(TokenType.OR)) {
            Token op = prev();
            ExprNode right = parseAndExpr();
            left = new BinaryOpNode(op.literal, left, right);
        }
        return left;
    }

    private ExprNode parseAndExpr() {
        log("parseAndExpr");
        ExprNode left = parseComparisonExpr();
        while (match(TokenType.AND)) {
            log("parseAndExpr-loop");
            Token op = prev();
            ExprNode right = parseComparisonExpr();
            left = new BinaryOpNode(op.literal, left, right);
            if (debugFlag) {
                System.out.println("    → and binary expr built: " + op.literal);
            }
        }
        return left;
    }

    private ExprNode parseComparisonExpr() {
        log("parseComparisonExpr");
        ExprNode left = parseUnaryExpr();

        while (match(TokenType.IS, TokenType.IS_NOT, TokenType.NOT_IS, TokenType.IN, TokenType.NOT_IN,
                TokenType.LIKE, TokenType.NOT_LIKE, TokenType.BETWEEN, TokenType.NOT_BETWEEN,
                TokenType.EQ, TokenType.NE, TokenType.LT, TokenType.LE, TokenType.GT, TokenType.GE)) {

            Token op = prev();
            ExprNode right;

            // between / in 的右值必须是元组
            if (op.type == TokenType.BETWEEN || op.type == TokenType.NOT_BETWEEN ||
                    op.type == TokenType.IN || op.type == TokenType.NOT_IN) {
                right = parseTuple(); // ✅ 特殊处理
            } else {
                right = parseUnaryExpr();
                if (debugFlag) {
                    System.out.println("    → comparison right expr parsed, pos=" + pos + ", token=" + peek());
                }
            }

            left = new BinaryOpNode(op.literal, left, right);
        }

        return left;
    }

    private ExprNode parseUnaryExpr() {
        // NOT 操作符 - 处理一元NOT表达式
//        if (match(TokenType.NOT)) {
//            Token op = prev();
//            ExprNode operand = parseUnaryExpr();
//            return new UnaryOpNode(op.literal, operand);
//        }
        return parsePrimaryExpr();
    }

    private ExprNode parsePrimaryExpr() {
        log("parsePrimaryExpr");
        // 如果匹配到左括号
        if (match(TokenType.LPAREN)) {
            ExprNode expr = parseExpression();
            consume(TokenType.RPAREN, "Expect ')' to close expression.");
            return expr;
        }
        // 匹配到变量名、函数名、或者函数参数
        else if (match(TokenType.IDENT)) {
            Token ident = prev();
            if (match(TokenType.LPAREN)) {
                return parseFunctionCall(ident.literal);
            } else {
                return new VariableNode(ident.literal);
            }
        }
        // 匹配到字符串
        else if (match(TokenType.STRING)) {
            return new LiteralNode(prev().literal);
        }
        // 匹配到数值
        else if (match(TokenType.NUMBER)) {
            return new LiteralNode(Numbers.toNumber(prev().literal));
        }
        // 匹配到关键字
        else if (match(TokenType.KEYWORD)) {
            String literal = prev().literal;
            if (literal.equalsIgnoreCase("true")) {
                return new LiteralNode(true);
            } else if (literal.equalsIgnoreCase("false")) {
                return new LiteralNode(false);
            } else if (literal.equalsIgnoreCase("null")) {
                return new LiteralNode(null);
            } else {
                return new LiteralNode(literal);
            }
        }
        // 匹配失败
        else {
            throw new RuntimeException("Unexpected token '" + peek().literal + "' at position " + pos);
        }
    }

    /**
     * 解析元组
     */
    private TupleNode parseTuple() {
        log("parseTuple");
        if (!match(TokenType.LPAREN)) {
            throw new RuntimeException("Expect '(' to start tuple.");
        }

        List<ExprNode> items = new ArrayList<>();
        if (!check(TokenType.RPAREN)) {
            do {
                items.add(parseComparisonExpr());
            } while (match(TokenType.COMMA));
        }

        consume(TokenType.RPAREN, "Expect ')' to close tuple.");
        return new TupleNode(items);
    }


    private ExprNode parseFunctionCall(String functionName) {
        log("parseFunctionCall");
        List<ExprNode> args = new ArrayList<>();
        if (!check(TokenType.RPAREN)) {
            do {
                args.add(parseComparisonExpr());
            } while (match(TokenType.COMMA));
        }
        consume(TokenType.RPAREN, "Expect ')' after function arguments.");
        return new FunctionCallNode(functionName, args);
    }


    private boolean match(TokenType... types) {
        for (TokenType type : types) {
            if (check(type)) {
                advance();
                return true;
            }
        }
        return false;
    }

    private boolean check(TokenType type) {
        if (isAtEnd()) return false;
        return peek().type == type;
    }

    private Token advance() {
        if (!isAtEnd()) pos++;
        if (debugFlag) {
            System.out.println("→ advance to pos=" + pos + ", token=" + peek());
        }
        return prev();
    }

    private boolean isAtEnd() {
        return peek().type == TokenType.EOF;
    }

    private Token peek() {
        return tokens.get(pos);
    }

    private Token prev() {
        return tokens.get(pos - 1);
    }

    private void consume(TokenType type, String message) {
        if (check(type)) {
            advance();
        } else {
            throw new RuntimeException(message + " But got: " + peek());
        }
    }

    /**
     * 日志打印
     */
    private void log(String from) {
        if (debugFlag) {
            System.out.printf("[%-20s] pos=%-3d | token=%-15s (%s)%n", from, pos, peek().type, peek().literal);
        }
    }
}
